﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OOAD_Project_Engine.Foundation.Interfaces;
using OOAD_Project_Engine.Foundation.MyEventArgs;

namespace OOAD_Project_Engine.Domain.Entities
{
    public abstract class EntityCollection : IEntityCollection
    {
        Dictionary<string, List<IEntity>> entities = new Dictionary<string, List<IEntity>>(); // name, all occurences of entities with this name (case-insensitive)
        List<IEntity> allEntities = new List<IEntity>();

        public IEntity Find(string name)
        {
            if (entities.ContainsKey(name))
            {
                foreach (IEntity entity in entities[name])
                {
                    return entity;
                }
            }
            return null;
        }

        public IEntity FindWith<T>() where T : IEntityComponent
        {
            foreach (IEntity entity in allEntities)
            {
                if (entity.Contains<T>())
                {
                    return entity;
                }
            }
            return null;
        }

        public List<IEntity> FindAllWith<T>() where T : IEntityComponent
        {
            List<IEntity> matches = new List<IEntity>();
            foreach (IEntity entity in allEntities)
            {
                if (entity.Contains<T>())
                {
                    matches.Add(entity);
                }
            }
            return matches;
        }

        public T Find<T>(string name) where T : IEntityComponent
        {
            if (entities.ContainsKey(name))
            {
                foreach (IEntity entity in entities[name])
                {
                    return entity.Find<T>();
                }
            }
            return default(T);
        }

        public bool Remove(string name)
        {
            return Remove(Find(name));
        }

        public bool Remove(IEntity item)
        {
            if (item == null)
            {
                return false;
            }

            bool result = false;

            if (allEntities.Contains(item))
            {
                allEntities.Remove(item);
            }

            if (entities.ContainsKey(item.Name))
            {
                if (entities[item.Name].Contains(item))
                {
                    result = entities[item.Name].Remove(item);

                    OnEntityRemoved(new EntityEventArgs(item));
                }
                if (!(entities[item.Name].Count > 0))
                {
                    entities.Remove(item.Name);
                }
            }

            return result;
        }

        public void Add(IEntity item)
        {
            if (item.Manager == null)
            {
                throw new ArgumentException("Cannot add an un-managed entity.");
            }

            if (allEntities.Contains(item))
            {
                throw new ArgumentException("Cannot add a reference to an instance that is already in the list.");
            }

            allEntities.Add(item);

            if (entities.ContainsKey(item.Name))
            {
                entities[item.Name].Add(item);
            }
            else
            {
                entities.Add(item.Name, new List<IEntity>(new IEntity[] { item }));
            }

            item.Initialize();

            item.ComponentAdded += onComponentAdded;
            item.ComponentRemoved += onComponentRemoved;
            OnEntityAdded(new EntityEventArgs(item));
        }

        void onComponentAdded(object sender, EntityComponentEventArgs e)
        {
            e.Component.Initialize();
        }

        void onComponentRemoved(object sender, EntityComponentEventArgs e)
        {

        }

        public void Clear()
        {
            foreach (IEntity entity in allEntities)
            {
                Remove(entity);
            }
        }

        public bool Contains(IEntity item)
        {
            return allEntities.Contains(item);
        }

        public void CopyTo(IEntity[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        public int Count
        {
            get
            {
                return allEntities.Count;
            }
        }

        public bool IsReadOnly
        {
            get;
            set;
        }

        public event EventHandler<EntityEventArgs> EntityAdded;

        protected virtual void OnEntityAdded(EntityEventArgs e)
        {
            if (EntityAdded != null)
            {
                EntityAdded(this, e);
            }
        }

        public event EventHandler<EntityEventArgs> EntityRemoved;

        protected virtual void OnEntityRemoved(EntityEventArgs e)
        {
            if (EntityRemoved != null)
            {
                EntityRemoved(this, e);
            }
        }

        public IEnumerator<IEntity> GetEnumerator()
        {
            return allEntities.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return allEntities.GetEnumerator();
        }

        public IEntity[] ToArray()
        {
            return allEntities.ToArray();
        }
    }
}
